/**
 * 参考：
 *      https://justinmeiners.github.io/lc3-DEV/src/lc3-alt.cpp
 *      https://arthurchiao.art/blog/write-your-own-virtual-machine-zh/
 */
#include <iostream>
#include <signal.h>

#include "include/lc3.h"
#include "include/dev.h"
#include "include/disk.hpp"
#include "include/mem.hpp"
#include "include/cpu.hpp"
#include "include/bus.hpp"

typedef LC3                                             arch_t ;
typedef MDev                                            mdev_t ;
typedef DISK<FILE>                                      disk_t ;
typedef MEM<uint16_t>                                   mem_t  ;
typedef CPU<uint16_t, arch_t>                           cpu_t  ;
typedef BUS<uint16_t, cpu_t, mem_t, disk_t, mdev_t>     bus_t  ;

/**
 * 自定义中断处理
 * 用来强制关闭虚拟机
 */
void handle_interrupt(int signal); 

/** 
 * 全局变量
 * 实例化一台虚拟机
 * 然后向总线注册必要的设备
 */
disk_t   vm_disk ;              // 磁盘
Terminal vm_dev  ;              // 外设
// mdev_t   vm_dev  ;              // 外设
mem_t    vm_mem  ;              // 内存
cpu_t    vm_cpu  ;              // CPU


BUS_TEMPLATE
cpu_t&   BUS_CLASS::cpu = vm_cpu;
BUS_TEMPLATE
mem_t&   BUS_CLASS::mem = vm_mem;
BUS_TEMPLATE
disk_t&  BUS_CLASS::disk = vm_disk;
BUS_TEMPLATE
mdev_t&  BUS_CLASS::dev = vm_dev;

int main(int argc, const char* argv[]) 
{
    /* 载入参数 */
    if ( 2 != argc ) {
        std::cout << "main [image-file1] " << std::endl;
        exit(2);
    }

    /* 初始化 */
    vm_mem.init();
    vm_cpu.init();
    bus_t::init();
    
    /* 读磁盘文件 */
    for (int i = 1; i < argc; ++i) {
        if( !vm_disk.read_file(argv[i]) ) {
            std::cout << "failed to load image: " << argv[i] << std::endl;
            exit(1);
        }
        vm_mem.load_image();  // 将二进制可执行文件载入内存
    }

    /* 注册物理机中断 */
    signal(SIGINT, handle_interrupt);

    /* 设置虚拟机终端 */
    vm_dev.set_terminal();

    /* 执行 */
    while( vm_cpu.state() ) {
        vm_cpu.fetch();
        vm_cpu.decode();       
        vm_cpu.run();              
    }

    /**
     * 虚拟机正常终止 
     * 恢复物理机原来的终端属性
     * 并输出一些提示信息
     */
    vm_dev.restore_terminal();
    std::cout << std::endl;
    std::cout << "DEV normal shutdown ..." << std::endl;
    return 0;
}

/**
 * @brief : 
 *      自定义中断处理
 *      接收 Ctrl+C 物理中断，强制关闭虚拟机
 */ 
void handle_interrupt(int signal) 
{
    /* 恢复物理机原来的终端属性 */
    vm_dev.restore_terminal();  

    /* 输出一些提示信息 */   
    std::cout << std::endl;
    std::cout << "DEV forced shutdown ..." << std::endl;
    fflush(stdout);
    fflush(stdin);
    exit(-2);
}
